using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Management;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace DiskSpace
{
    public partial class Info : Form
    {
        private Thread infoThread;
        private Int32 m_updateSerial = 0;
        private Int32 m_diskInfoHeight = 0;
        private Int32 m_diskInfoWidth = 0;
        private Label m_statusLabel = new Label();

        private DiskInfo[] m_diskInfo;
        private Dictionary<String,DiskInformation> m_diskInformation = new Dictionary<String,DiskInformation>();
        private System.Windows.Forms.Timer m_refreshDisplayTimer = new System.Windows.Forms.Timer();
        private System.Windows.Forms.Timer m_updateStatusTimer = new System.Windows.Forms.Timer();

        private IConfig m_config = null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="config"></param>
        public Info(IConfig config)
        {
            // Determine the height of the disk info objects we will use on the form
            // The form is dynamically sized depending on the number of disks.
            DiskInfo tempDi = new DiskInfo();
            m_diskInfoHeight = tempDi.Height;
            m_diskInfoWidth = tempDi.Width;

            // Configure the basic status label for no information available yet
            m_statusLabel.Width = m_diskInfoWidth;
            m_statusLabel.Height = m_diskInfoHeight;
            m_updateStatusTimer.Interval = 100;
            m_updateStatusTimer.Tick += UpdateStatusLabelHandler;

            // Store the reference for the config interface as we will use values from this
            m_config = config;

            InitializeComponent();

            // How often do we want the screen to update
            m_refreshDisplayTimer.Interval = 1000;
            m_refreshDisplayTimer.Tick += new EventHandler(UpdateDisplayTickHandler);
            m_refreshDisplayTimer.Start();

            // The data is read from the management system in a different thread.
            infoThread = new Thread(new ThreadStart(DiskInfoUpdateThread));
            infoThread.IsBackground = true;
            infoThread.Start();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Info_Load(object sender, EventArgs e)
        {
            Hide();
            UpdateFormLocation();

            //prevent the title bar from displaying
            ControlBox = false;
            Text = string.Empty;

            UpdateFormLocation();
            Show();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void UpdateDisplayTickHandler(object sender, EventArgs e)
        {
            UpdateDisplay();
        }

        /// <summary>
        /// 
        /// </summary>
        private void DiskInfoUpdateThread()
        {
            while (true)
            {
                ReadDiskInfo();
                Thread.Sleep(m_config.UpdateTime);
            }
        }

        private void ReadDiskInfo2()
        {
            DriveInfo[] drives = DriveInfo.GetDrives();

            m_updateSerial++;
            foreach (DriveInfo di in drives)
            {
                DiskInformation diskInformation = new DiskInformation();

                diskInformation.Label = di.VolumeLabel;
                diskInformation.Volume = di.Name;
                diskInformation.Size = di.TotalSize;
                diskInformation.Free = di.TotalFreeSpace;
                diskInformation.UpdateSerial = m_updateSerial;

                switch (di.DriveType)
                {
                    case DriveType.Fixed:
                        diskInformation.Type = DiskInformation.DriveType.Fixed;
                        break;

                    case DriveType.Removable:
                        diskInformation.Type = DiskInformation.DriveType.Removeable;
                        break;

                    default:
                        diskInformation.Type = DiskInformation.DriveType.Unknown;
                        break;
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private void ReadDiskInfo()
        {
            SelectQuery query;

            //query = new SelectQuery("select Name, DeviceID, VolumeName, Size, FreeSpace, driveType from win32_logicaldisk");
            query = new SelectQuery("select Name, DeviceID, VolumeName, Size, FreeSpace, driveType from win32_logicaldisk where drivetype='2' OR drivetype='3'");
 
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
            m_updateSerial++;
            foreach (ManagementObject mo in searcher.Get())
            {
                try
                {
                    String disk = mo["DeviceId"].ToString();
                    Int64 diskSize = Convert.ToInt64(mo["Size"].ToString());
                    String label = mo["VolumeName"].ToString();
                    Int64 free = Convert.ToInt64(mo["FreeSpace"].ToString());
                    String type = mo["driveType"].ToString();

                    if (!m_diskInformation.ContainsKey(disk))
                    {
                        m_diskInformation.Add(disk, new DiskInformation());
                    }

                    m_diskInformation[disk].Label = label;
                    m_diskInformation[disk].Volume = disk;
                    m_diskInformation[disk].Size = diskSize;
                    m_diskInformation[disk].Free = free;
                    m_diskInformation[disk].UpdateSerial = m_updateSerial;

                    switch (type)
                    {
                        case "3":
                            m_diskInformation[disk].Type = DiskInformation.DriveType.Fixed;
                            break;

                        case "2":
                            m_diskInformation[disk].Type = DiskInformation.DriveType.Removeable;
                            break;

                        default:
                            m_diskInformation[disk].Type = DiskInformation.DriveType.Unknown;
                            break;
                    }
                }
                catch
                {
                }
            }

            searcher.Dispose();

            // now we have updated all of the drives in the system we need to account for any that are no longer
            // connected. This may be due to them being removed. If this is the case the serial number above will
            // not have been udpated.
            foreach (KeyValuePair<String, DiskInformation> di in m_diskInformation)
            {
                // if the serial number is not updated then we will mark the disk as removed.
                if (di.Value.UpdateSerial != m_updateSerial)
                {
                    di.Value.Type = DiskInformation.DriveType.Removed;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenDisk(object sender, DiskClickEventArgs e)
        {
            Process.Start(e.DiskName);
        }

        private Int32 m_statusCount = 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateStatusLabelHandler(object sender, EventArgs e)
        {
            String dotString = new String('.',m_statusCount % 10);

            m_statusLabel.Text = String.Concat("Loading Disk Information", dotString);
            m_statusCount += 1;
        }

        /// <summary>
        /// 
        /// </summary>
        private void UpdateDisplay()
        {
            Int32 i = 0;

            // If we have not added any controls to the form then we should add a holding message until we have read some data from the disks
            if (Controls.Count == 0)
            {
                DisplayPlaceHolderMessage();
            }

            // if we do not have anything to display yet then just return
            if (m_diskInformation.Count == 0)
            {
                return;
            }
            else
            {
                RemovePlaceHolderMessage();
            }

            int itemCount = NumberOfDrivesForDisplay();
            if (m_diskInfo == null || itemCount > m_diskInfo.Length)
            {
                m_diskInfo = new DiskInfo[itemCount];
            }

            // check if we need to add or remove controls from the form
            if (itemCount != Controls.Count)
            {
                this.ClientSize = new Size(m_diskInfoWidth, (m_diskInfoHeight * itemCount));

                while (Controls.Count > itemCount)
                {
                    Controls.RemoveAt(Controls.Count - 1);
                }

                while (Controls.Count < itemCount)
                {
                    DiskInfo di = new DiskInfo();
                    di.DiskClick += OpenDisk;
                    Controls.Add(di);
                }
            }

            // now we need to display all of the disk information for disks that are selected to be shown
            foreach (KeyValuePair<String, DiskInformation> di in m_diskInformation)
            {
                try
                {
                    if (IsDiskVisible(di.Value))
                    {
                        m_diskInfo[i] = new DiskSpace.DiskInfo();
                        DiskInfo diskInfo = (DiskInfo)Controls[i];

                        diskInfo.Disk = di.Value.Volume;
                        diskInfo.DiskSize = di.Value.Size;
                        diskInfo.Label = di.Value.Label;
                        diskInfo.Location = new System.Drawing.Point(0, m_diskInfoHeight * i);
                        diskInfo.Space = di.Value.Free;
                        diskInfo.AlarmThreshold = m_config.AlarmThreshold;
                        diskInfo.WarningThreshold = m_config.WarningThreshold;
                        diskInfo.ShowLabels = m_config.DisplayDiskLabels;

                        i++;
                    }
                }
                catch
                {
                }
            }

            UpdateFormLocation();
            this.Update();
        }

        /// <summary>
        /// 
        /// </summary>
        private void RemovePlaceHolderMessage()
        {
            // lets stop the timer and remove the holding message if it is still there
            if (Controls[0].GetType() == m_statusLabel.GetType())
            {
                m_updateStatusTimer.Stop();
                Controls.Clear();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void DisplayPlaceHolderMessage()
        {
            m_updateStatusTimer.Start();
            Controls.Add(m_statusLabel);
            ClientSize = new Size(m_diskInfoWidth, m_diskInfoHeight);

            UpdateFormLocation();
            this.Update();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemCount"></param>
        /// <returns></returns>
        private int NumberOfDrivesForDisplay()
        {
            int itemCount = 0;

            // quick pre-check to determine how many items we are going to add and make the form the correct size
            // we will only change the size if it has changed from the last time
            foreach (KeyValuePair<String, DiskInformation> di in m_diskInformation)
            {
                if (IsDiskVisible(di.Value))
                {
                    itemCount++;
                }
            }
            return itemCount;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="di"></param>
        /// <returns></returns>
        private bool IsDiskVisible(DiskInformation di)
        {
            bool visible = false;

            switch (di.Type)
            {
                case DiskInformation.DriveType.Fixed:
                    visible = true;
                    break;
                case DiskInformation.DriveType.Removeable:
                    if (m_config.IncludeRemoveable)
                    {
                        visible = true;
                    }
                    break;
                default:
                    visible = true;
                    break;
            }

            return visible;
        }

        /// <summary>
        /// 
        /// </summary>
        private void UpdateFormLocation()
        {
            Screen primaryScreen = null;

            foreach (Screen screen in Screen.AllScreens)
            {
                if (screen.Primary == true)
                {
                    primaryScreen = screen;
                    break;
                }
            }
            if (primaryScreen != null)
            {
                Location = new Point(primaryScreen.WorkingArea.Width - Size.Width, primaryScreen.WorkingArea.Height - Size.Height);
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Info_MouseClick(object sender, MouseEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Info_Click(object sender, EventArgs e)
        {
            Close();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Info_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 'a')
            {
                e.Handled = true;
                Hide();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Info_VisibleChanged(object sender, EventArgs e)
        {
            if (Visible == true)
            {
                UpdateDisplay();
            }
        }
    }
}